#include "../include/lib.plugin.h"
#include "../../Base/lib/lib.log.h"
#include "../../Base/lib/lib.dlt.h"
#include "../../Base/lib/lib.time.h"

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

#ifdef OS_LINUX
#include <dlfcn.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#endif

#define SH_MEM_SIZE 11+1+(p_dkl_daemon->length)*(SHM_MEM_SIZE_BASE_PER_PLUGIN)


static dLinkList *pListPlugin;

#ifdef OS_LINUX
static key_t ktDaemon;
static key_t ktCycle;

static key_t ktSemPlugin;

#endif

dLinkList* initPlugins(hTab *hcf) {
    char pluginPath[MAX_LENGTH_OF_FILELOCATION];
    char *value;
    char *pluginLog;
#ifdef OS_LINUX
    DIR *dir;
    struct dirent *dEntry;
#endif

    pListPlugin=NULL;

	if(NULL==(pluginLog=getPluginLogDIR()))
		pluginLog=getSTDLogDIR();

    logIt(LOG_INFO,"Entering initializing plugins");
	logIt(LOG_INFO,"Refer to %s for plugin log details",pluginLog);

    if (NULL!=(value=(char *)getHashItem(hcf,"WASD_PLUGIN_PATH"))) {
        return NULL;
    }
    strcat(pluginPath,value);

#ifdef OS_LINUX
    dir=NULL;

    if (NULL==(dir=opendir(pluginPath))) {
        logIt(LOG_ERROR,"Open plugin folder failed");
        return NULL;
    }

    errno=0;
    if (NULL==(pListPlugin=initDLinkList())) {
        logIt(LOG_ERROR,"Initialize plugin list failed.");
        closedir(dir);
        return NULL;
    }

    dEntry=NULL;

	logPlugin(LOG_DEBUG,"Start to load plugins");

	logPlugin(LOG_INFO,"Start to Initalize plugins");

    while (NULL!=(dEntry=readdir(dir))) {
        static char pluginSoPath[MAX_LENGTH_OF_FILELOCATION];
        DIR *dpSo;
        pluginItem *ppi;

        char *errPluginFn;
        static BOOL (*getPluginRequire)();
        static BOOL (*execPlugin)();

        if (0==dEntry->d_ino)
            continue;
        if (!strcmp(dEntry->d_name,".")||!strcmp(dEntry->d_name,".."))
            continue;

        memset(pluginSoPath,'\0',MAX_LENGTH_OF_FILELOCATION);

		logPlugin(LOG_DEBUG,"Load %s.so",dEntry->d_name);

        sprintf(pluginSoPath,"%s/%s/%s.so",pluginPath,dEntry->d_name,dEntry->d_name);
        if (NULL==(ppi=(pluginItem *)malloc(sizeof(pluginItem)))) {
        	logPlugin(LOG_ERROR,"Load \"%s\"failed, cannot malloc pluginItem for it. The system will startup without it.",dEntry->d_name);
        	logIt(LOG_ERROR,"Fail to malloc memory during plugin intialization. Exiting....");
        	goto release_resource;
        }

        memset(ppi->pluginName,'\0',sizeof(ppi->pluginName));
        strcpy(ppi->pluginName,dEntry->d_name);

        if(NULL==(ppi->handler=dlopen(pluginSoPath, RTLD_LAZY))) {
            FREEUP(ppi);
            logIt(LOG_ERROR,"Load \"%s\" failed, cannot open the .so file for it. The system will startup without it.",dEntry->d_name);
            continue;
        }

        getPluginRequire=dlsym(ppi->handler,"getPluginRequire");
        if(NULL!=(errPluginFn=dlerror())) {
            logIt(LOG_DEBUG,"Plugin %s initializing failed in calling getPluginRequire",dEntry->d_name);
            dlclose(ppi->handler);
            FREEUP(ppi);
            continue;
        }
        ppi->isRequired=getPluginRequire();


        execPlugin=dlsym(ppi->handler,"execPlugin");
        if(NULL!=(errPluginFn=dlerror())) {
            logIt(LOG_DEBUG,"Plugin %s initializing failed in getting execPlugin",dEntry->d_name);
            dlclose(ppi->handler);
            FREEUP(ppi);
            continue;
        }

        if(FALSE==execPlugin()){
        	logPlugin(LOG_ERROR,"Plugin %s Exec failed",dEntry->d_name);
        	if(TRUE==ppi->isRequired){
        		logPlugin(LOG_ERROR,"Plugin %s is marked as required. The daemon cannot startup. Exiting....");
        		FREEUP(ppi);
        		goto release_resource;
        	}else{
        		logPlugin(LOG_ERROR,"Plugin %s is not marked as required. Startup without it");
        		FREEUP(ppi);
        		continue;
        	}
        }

        ppi->lastRun=getUTCNow();
        addDLinkListItem(pListPlugin,dEntry->d_name,(void *)ppi);


    }

    logPlugin(LOG_INFO,"Finish initializing plugins");

    return pListPlugin;

#endif


release_resource:
	freeDLinkList(pListPlugin,TRUE);
    return NULL;

}


BOOL shutDownPlugins() {
	return TRUE;
}


/*! @fn
        closePlugins
    @description
        Release all resources related to plugins. E.g. the queue for the plugin
*/

BOOL closePlugins() {
	return TRUE;
}



dLinkList* getPluginList() {
    return pListPlugin;
}
